package com.sweet.encrypt;

import com.sweet.enums.Charset;

import java.io.UnsupportedEncodingException;

/**
 * Base64加密、解密
 *
 * @author limi
 * @date 2020-01-03
 * @since JDK1.8
 */
public final class Base64 {
    private Base64() {
    }

    public static void main(String[] args) {
        checkBase64("eyJhY2NvdW50IjoiaGR6aGFuZyIsImV4cCI6MTY2NjM1NDU3NSwibmFtZSI6IuW8oOa1t" +
                "-S4nCIsIm5pY2siOiLmmajoiKrpo44ifQ");
    }

    /**
     * 解码Base64
     *
     * @param data
     */
    public static void checkBase64(String data) {
        for (Charset charset : Charset.values()) {
            for (CoderTypeEnum codeType : CoderTypeEnum.values()) {
                try {
                    System.out.println("[" + charset.getCharset() + "],类型[" + codeType + "]=" + decodeToString(codeType, data, charset));
                } catch (Exception e) {
                    System.out.println("[" + charset.getCharset() + "],类型[" + codeType + "]=异常");
                }
            }
        }
    }

    /**
     * 使用基础编码
     * 描述详见CoderTypeEnum.BASE
     *
     * @param text    文本数据
     * @param charset 编码字符
     * @return
     * @throws UnsupportedEncodingException
     */
    public static byte[] encode(String text, Charset charset) throws UnsupportedEncodingException {
        return encode(CoderTypeEnum.BASE, text, charset);
    }

    /**
     * 使用基础编码
     * 描述详见CoderTypeEnum.BASE
     *
     * @param binaryData 字节数据
     * @param charset    编码字符
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String encodeToString(byte[] binaryData, Charset charset) throws UnsupportedEncodingException {
        return encodeToString(CoderTypeEnum.BASE, binaryData, charset);
    }

    /**
     * 使用基础编码
     * 描述详见CoderTypeEnum.BASE
     *
     * @param text    文本数据
     * @param charset 编码字符
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String encodeToString(String text, Charset charset) throws UnsupportedEncodingException {
        return encodeToString(CoderTypeEnum.BASE, text, charset);
    }

    /**
     * 使用基础解码
     *
     * @param text    文本数据
     * @param charset 编码字符
     * @return
     * @throws UnsupportedEncodingException
     */
    public static byte[] decode(String text, Charset charset) throws UnsupportedEncodingException {
        return decode(CoderTypeEnum.BASE, text, charset);
    }

    /**
     * 使用基础解码
     *
     * @param binaryData 字节数据
     * @param charset    编码字符
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String decodeToString(byte[] binaryData, Charset charset) throws UnsupportedEncodingException {
        return decodeToString(CoderTypeEnum.BASE, binaryData, charset);
    }

    /**
     * 使用基础解码
     *
     * @param text    文本数据
     * @param charset 编码字符
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String decodeToString(String text, Charset charset) throws UnsupportedEncodingException {
        return decodeToString(CoderTypeEnum.BASE, text, charset);
    }

    /**
     * 使用URL编码
     * 描述详见CoderTypeEnum.URL
     *
     * @param text    待编码的文本
     * @param charset 编码字符
     * @return
     * @throws UnsupportedEncodingException
     */
    public static byte[] urlEncode(String text, Charset charset) throws UnsupportedEncodingException {
        return encode(CoderTypeEnum.URL, text, charset);
    }

    /**
     * 使用URL编码
     * 描述详见CoderTypeEnum.URL
     *
     * @param binaryData 字节数据
     * @param charset    编码字符
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String urlEncodeToString(byte[] binaryData, Charset charset) throws UnsupportedEncodingException {
        return encodeToString(CoderTypeEnum.URL, binaryData, charset);
    }

    /**
     * 使用URL编码
     * 描述详见CoderTypeEnum.URL
     *
     * @param text    待编码的文本
     * @param charset 编码字符
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String urlEncodeToString(String text, Charset charset) throws UnsupportedEncodingException {
        return encodeToString(CoderTypeEnum.URL, text, charset);
    }

    /**
     * 使用URL解码
     *
     * @param text
     * @param charset
     * @return
     * @throws UnsupportedEncodingException
     */
    public static byte[] urlDecode(String text, Charset charset) throws UnsupportedEncodingException {
        return decode(CoderTypeEnum.URL, text, charset);
    }

    /**
     * 使用URL解码
     *
     * @param binaryData 字节数据
     * @param charset
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String urlDecodeToString(byte[] binaryData, Charset charset) throws UnsupportedEncodingException {
        return decodeToString(CoderTypeEnum.URL, binaryData, charset);
    }

    /**
     * 使用URL解码
     *
     * @param text
     * @param charset
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String urlDecodeToString(String text, Charset charset) throws UnsupportedEncodingException {
        return decodeToString(CoderTypeEnum.URL, text, charset);
    }

    /**
     * 使用MIME编码
     * 描述详见CoderTypeEnum.MIME
     *
     * @param text    待编码的文本
     * @param charset 编码字符
     * @return
     * @throws UnsupportedEncodingException
     */
    public static byte[] mimeEncode(String text, Charset charset) throws UnsupportedEncodingException {
        return encode(CoderTypeEnum.MIME, text, charset);
    }

    /**
     * 使用MIME编码
     * 描述详见CoderTypeEnum.MIME
     *
     * @param binaryData 字节数据
     * @param charset    编码字符
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String mimeEncodeToString(byte[] binaryData, Charset charset) throws UnsupportedEncodingException {
        return encodeToString(CoderTypeEnum.MIME, binaryData, charset);
    }

    /**
     * 使用MIME编码
     * 描述详见CoderTypeEnum.MIME
     *
     * @param text    待编码的文本
     * @param charset 编码字符
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String mimeEncodeToString(String text, Charset charset) throws UnsupportedEncodingException {
        return encodeToString(CoderTypeEnum.MIME, text, charset);
    }

    /**
     * 使用MIME解码
     *
     * @param text
     * @param charset
     * @return
     * @throws UnsupportedEncodingException
     */
    public static byte[] mimeDecode(String text, Charset charset) throws UnsupportedEncodingException {
        return decode(CoderTypeEnum.MIME, text, charset);
    }

    /**
     * 使用MIME解码
     *
     * @param binaryData 字节数据
     * @param charset
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String mimeDecodeToString(byte[] binaryData, Charset charset) throws UnsupportedEncodingException {
        return decodeToString(CoderTypeEnum.MIME, binaryData, charset);
    }

    /**
     * 使用MIME解码
     *
     * @param text
     * @param charset
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String mimeDecodeToString(String text, Charset charset) throws UnsupportedEncodingException {
        return decodeToString(CoderTypeEnum.MIME, text, charset);
    }

    /**
     * 编码
     *
     * @param coderType 编码类型
     * @param text      编码内容
     * @param charset   编码字符类型
     * @return
     * @throws UnsupportedEncodingException
     */
    private static byte[] encode(CoderTypeEnum coderType, String text, Charset charset) throws UnsupportedEncodingException {
        return CoderTypeEnum.getEncoder(coderType).encode(text.getBytes(charset.getCharset()));
    }

    /**
     * 编码
     *
     * @param coderType  编码类型
     * @param binaryData 解码字节
     * @param charset    编码字符类型
     * @return
     * @throws UnsupportedEncodingException
     */
    private static String encodeToString(CoderTypeEnum coderType, byte[] binaryData, Charset charset) throws UnsupportedEncodingException {
        return new String(CoderTypeEnum.getEncoder(coderType).encode(binaryData), charset.getCharset());
    }

    /**
     * 编码
     *
     * @param coderType 编码类型
     * @param text      编码内容
     * @param charset   编码字符类型
     * @return
     * @throws UnsupportedEncodingException
     */
    private static String encodeToString(CoderTypeEnum coderType, String text, Charset charset) throws UnsupportedEncodingException {
        return encodeToString(coderType, text.getBytes(charset.getCharset()), charset);
    }

    /**
     * 解码
     *
     * @param coderType 解码类型
     * @param text      解码内容
     * @param charset   解码字符类型
     * @return
     * @throws UnsupportedEncodingException
     */
    private static byte[] decode(CoderTypeEnum coderType, String text, Charset charset) throws UnsupportedEncodingException {
        return CoderTypeEnum.getDecoder(coderType).decode(text.getBytes(charset.getCharset()));
    }

    /**
     * 解码
     *
     * @param coderType  解码类型
     * @param binaryData 解码字节
     * @param charset    解码字符类型
     * @return
     * @throws UnsupportedEncodingException
     */
    private static String decodeToString(CoderTypeEnum coderType, byte[] binaryData, Charset charset) throws UnsupportedEncodingException {
        return new String(CoderTypeEnum.getDecoder(coderType).decode(binaryData), charset.getCharset());
    }

    /**
     * 解码
     *
     * @param coderType 解码类型
     * @param text      解码内容
     * @param charset   解码字符类型
     * @return
     * @throws UnsupportedEncodingException
     */
    private static String decodeToString(CoderTypeEnum coderType, String text, Charset charset) throws UnsupportedEncodingException {
        return decodeToString(coderType, text.getBytes(charset.getCharset()), charset);
    }

    /**
     * 编码/解码类型
     *
     * @author limi
     * @date 2020-11-20 14:30
     */
    private enum CoderTypeEnum {
        /**
         * 使用基础编码
         * 输出被映射到一组字符A-Za-z0-9+/
         * 编码不添加任何行标，输出的解码仅支持A-Za-z0-9+/
         */
        BASE,

        /**
         * 使用URL编码
         * 输出映射到一组字符A-Za-z0-9+_，输出是URL和文件
         */
        URL,

        /**
         * 使用MIME编码
         * 输出隐射到MIME友好格式
         * 输出每行不超过76字符，并且使用'\r'并跟随'\n'作为分割
         * 编码输出最后没有行分割
         */
        MIME;

        /**
         * 获取编码器
         *
         * @param coderType
         * @return
         */
        public static java.util.Base64.Encoder getEncoder(CoderTypeEnum coderType) {
            if (coderType == CoderTypeEnum.BASE) {
                return java.util.Base64.getEncoder();
            } else if (coderType == CoderTypeEnum.URL) {
                return java.util.Base64.getUrlEncoder();
            } else if (coderType == CoderTypeEnum.MIME) {
                return java.util.Base64.getMimeEncoder();
            } else {
                throw new RuntimeException("不支持该类型编码!");
            }
        }

        /**
         * 获取解码器
         *
         * @param coderType
         * @return
         */
        public static java.util.Base64.Decoder getDecoder(CoderTypeEnum coderType) {
            if (coderType == CoderTypeEnum.BASE) {
                return java.util.Base64.getDecoder();
            } else if (coderType == CoderTypeEnum.URL) {
                return java.util.Base64.getUrlDecoder();
            } else if (coderType == CoderTypeEnum.MIME) {
                return java.util.Base64.getMimeDecoder();
            } else {
                throw new RuntimeException("不支持该类型编码!");
            }
        }
    }
}